// progtest.cc 
//	Test routines for demonstrating that Nachos can load
//	a user program and execute it.  
//
//	Also, routines for testing the Console hardware device.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "console.h"
#include "addrspace.h"
#include "synch.h"
#include "string.h"

#define QUANTUM 100

//----------------------------------------------------------------------
// StartProcess
// 	Run a user program.  Open the executable, load it into
//	memory, and jump to it.
//----------------------------------------------------------------------
void
StartProcess(char *filename)
{
	//printf("START PROCESS\n");
    OpenFile *executable = fileSystem->Open(filename);
    AddrSpace *space;

    if (executable == NULL) {
	printf("Unable to open file %s\n", filename);
	return;
    }
   
    space = new AddrSpace(executable);

    currentThread->space = space;
	//printf("currentThread->space->numPages = %i; space->numPages = %i\n", currentThread->space->getNumPages(), space->getNumPages());
	//currentThread->startOfStack = currentThread->space->MakeStackPages();
	currentThread->startOfStack = (currentThread->space->getNumPages())*PageSize - 16;
	//printf("currentThread->startOfStack = %i\n", currentThread->startOfStack);
	//printf("START PROCESS: startOfStack = %i; PageSize = %i\n", currentThread->startOfStack, PageSize);
	currentThread->spaceId = nextProcessIndex; /* this process has a spaceId of 0 */
	currentThread->space->spaceID = nextProcessIndex;
	
	ProcessTableEntry* newProcessTableEntry = new ProcessTableEntry(currentThread->space);
	processTable[nextProcessIndex] = newProcessTableEntry;
	
	#ifdef NETWORK
	currentThread->setThreadId(processTable[nextProcessIndex]->getNumThreads());
	//processTable[nextProcessIndex]->incrementNumThreads();
	#else
	currentThread->setThreadId(processTable[nextProcessIndex]->numThreads);
	//processTable[nextProcessIndex]->numThreads++;
	#endif
	
	printf("startProcess: current thread spaceId = %i, threadId = %i\n", currentThread->spaceId, currentThread->getThreadId());
	
	numProcesses++;
	nextProcessIndex++;

	#ifndef USE_TLB
    delete executable;			// close file
	#endif
	
	//printf("space->InitRegisters();\n");
    space->InitRegisters();		// set the initial register values
	//printf("space->RestoreState();\n");
    space->RestoreState();		// load page table register

	//printf("run in main starts\n");
    machine->Run();			// jump to the user progam
	//printf("run in main done!!\n");
    ASSERT(FALSE);			// machine->Run never returns;
					// the address space exits
					// by doing the syscall "exit"
}

// Data structures needed for the console test.  Threads making
// I/O requests wait on a Semaphore to delay until the I/O completes.

static Console *console;
static Semaphore *readAvail;
static Semaphore *writeDone;

//----------------------------------------------------------------------
// ConsoleInterruptHandlers
// 	Wake up the thread that requested the I/O.
//----------------------------------------------------------------------

static void ReadAvail(int arg) { readAvail->V(); }
static void WriteDone(int arg) { writeDone->V(); }

//----------------------------------------------------------------------
// ConsoleTest
// 	Test the console by echoing characters typed at the input onto
//	the output.  Stop when the user types a 'q'.
//----------------------------------------------------------------------

void 
ConsoleTest (char *in, char *out)
{
    char ch;

    console = new Console(in, out, ReadAvail, WriteDone, 0);
    readAvail = new Semaphore("read avail", 0);
    writeDone = new Semaphore("write done", 0);
    
    for (;;) {
	readAvail->P();		// wait for character to arrive
	ch = console->GetChar();
	console->PutChar(ch);	// echo it!
	writeDone->P() ;        // wait for write to finish
	if (ch == 'q') return;  // if q, quit
    }
}

